/*
 *  Copyright (c) M2TK Project. All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package m2tk.assistant.app.ui.view;

import cn.hutool.core.util.StrUtil;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import m2tk.assistant.app.ui.component.DatagramPanel;
import m2tk.assistant.app.ui.task.AsyncQueryTask;
import m2tk.assistant.common.InfoView;
import m2tk.assistant.common.M2TKDatabase;
import m2tk.assistant.common.StreamObserver;
import m2tk.assistant.common.domain.*;
import m2tk.assistant.common.event.RefreshInfoViewRequest;
import m2tk.assistant.common.presets.StreamTypes;
import m2tk.assistant.common.ui.RoundPanel;
import net.miginfocom.swing.MigLayout;
import org.jdesktop.application.Application;
import org.kordamp.ikonli.fluentui.FluentUiRegularMZ;
import org.kordamp.ikonli.swing.FontIcon;
import org.pf4j.Extension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;

import static m2tk.assistant.common.ui.UIConstants.*;

@Extension(ordinal = 6)
public class DatagramView extends RoundPanel implements InfoView, StreamObserver
{
    private static final Logger log = LoggerFactory.getLogger(DatagramView.class);

    private DatagramPanel datagramPanel;

    private Application application;
    private M2TKDatabase database;
    private EventBus bus;

    private final String viewTitle = "数据结构";
    private final Icon viewIcon = FontIcon.of(FluentUiRegularMZ.TEXT_BULLET_LIST_TREE_24, ICON_SIZE_L, LIGHT_BLUE);
    private volatile long lastTimestamp;
    private final long MIN_QUERY_INTERVAL_MILLIS = 500;

    private static class DatagramContext
    {
        private Map<String, List<PrivateSection>> sections;
        private Map<Integer, List<TransportPacket>> transportPackets;
        private Map<Integer, List<PESPacket>> pesPackets;
    }

    public DatagramView()
    {
        initUI();
    }

    private void initUI()
    {
        datagramPanel = new DatagramPanel();

        setLayout(new MigLayout("insets 10", "[][grow][]", "[25!][pref!][][grow]"));
        add(new JLabel(viewTitle,
                       FontIcon.of(FluentUiRegularMZ.TEXT_BULLET_LIST_TREE_20, ICON_SIZE_S, LIGHT_BLUE),
                       SwingConstants.TRAILING),
            "left, wrap");
        add(separator(), "span 3, grow, wrap");
        add(datagramPanel, "span 3 2, grow");

        addComponentListener(new ComponentAdapter()
        {
            @Override
            public void componentShown(ComponentEvent e)
            {
                if (database != null)
                    queryDatagrams();
            }
        });
    }

    @Override
    public void setupApplication(Application application)
    {
        this.application = application;
    }

    @Override
    public void setupDataSource(EventBus bus, M2TKDatabase database)
    {
        this.bus = bus;
        this.database = database;

        bus.register(this);
    }

    @Override
    public JComponent getViewComponent()
    {
        return this;
    }

    @Override
    public String getViewTitle()
    {
        return viewTitle;
    }

    @Override
    public Icon getViewIcon()
    {
        return viewIcon;
    }

    @Override
    public List<JMenuItem> getContextMenuItem(ElementaryStream stream)
    {
        if (stream.isScrambled() ||
            StrUtil.contains(stream.getDescription(), "PES") ||
            StrUtil.equalsAny(stream.getCategory(),
                              StreamTypes.CATEGORY_VIDEO,
                              StreamTypes.CATEGORY_AUDIO,
                              StreamTypes.CATEGORY_NULL_PACKET))
            return List.of();

        JMenuItem item = new JMenuItem();
        item.setText("过滤私有段");
        item.addActionListener(e -> filterPrivateSection(stream));
        return List.of(item);
    }

    @Subscribe
    public void onRefreshInfoViewEvent(RefreshInfoViewRequest event)
    {
        long t1 = System.currentTimeMillis();
        if (t1 - lastTimestamp >= MIN_QUERY_INTERVAL_MILLIS && isShowing())
        {
            queryDatagrams();
            lastTimestamp = System.currentTimeMillis();
        }
    }

    private void queryDatagrams()
    {
        Supplier<DatagramContext> query = () -> {
            DatagramContext context = new DatagramContext();
            context.sections = database.getPrivateSectionGroups();
            context.transportPackets = database.getTransportPacketGroups();
            context.pesPackets = database.getPESPacketGroups();
            return context;
        };
        Consumer<DatagramContext> consumer = context ->
            datagramPanel.update(context.sections, context.transportPackets, context.pesPackets);

        AsyncQueryTask<DatagramContext> task = new AsyncQueryTask<>(application, query, consumer);
        task.execute();
    }

    private void filterPrivateSection(ElementaryStream stream)
    {
        int pid = stream.getStreamPid();
        if (pid == 0x1FFF)
        {
            String text = String.format("不支持的流类型：%s", stream.getDescription());
            JOptionPane.showMessageDialog(null, text);
            log.info(text);
            return;
        }

        log.info("添加私有段过滤器：'流{}'，类型：{}", stream.getStreamPid(), stream.getDescription());

        FilteringHook hook = new FilteringHook();
        hook.setSourceUri(database.getCurrentStreamSource().getUri());
        hook.setSubjectType("section");
        hook.setSubjectPid(stream.getStreamPid());
        hook.setSubjectTableId(-1);
        database.addFilteringHook(hook);
    }
}
